package com.hanxiaozhang.watermark.example1;


import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;


/**
 * 〈一句话功能简述〉<br>
 * 〈〉
 *
 * @author hanxinghua
 * @create 2024/7/13
 * @since 1.0.0
 */
public class ImageWatermarkUtil {


    /**
     * 获取水印信息并对画布做基础配置
     *
     * @param graphics
     * @param watermarkType
     * @param watermark
     * @param font
     * @param color
     * @return
     */
    public static WatermarkInfo getWatermarkInfo(Graphics2D graphics, WatermarkTypeEnum watermarkType, String watermark,
                                                 Font font, Color color) {
        WatermarkInfo watermarkInfo = new WatermarkInfo();
        // 文字水印
        if (WatermarkTypeEnum.TEXT_WATERMARK.equals(watermarkType)) {
            // 设置字体
            graphics.setFont(font);
            // 设置颜色
            graphics.setColor(color);
            watermarkInfo.setWatermarkWidth(getWatermarkLength(watermark, graphics));
            watermarkInfo.setWatermarkHeight(font.getSize());
        } else {
            // 图片水印BufferedImage对象
            BufferedImage watermarkBufferedImage = readPicture(watermark);
            watermarkInfo.setWatermarkBufferedImage(watermarkBufferedImage);
            watermarkInfo.setWatermarkWidth(watermarkBufferedImage.getWidth());
            watermarkInfo.setWatermarkHeight(watermarkBufferedImage.getHeight());
        }
        return watermarkInfo;
    }


    /**
     * 添加水印
     *
     * @param bufferedImage              源文件
     * @param font                       字体
     * @param color                      颜色
     * @param watermarkType              水印类型
     * @param imageWatermarkPositionType 水印位置类型
     * @param watermark                  水印（文字或图片地址）
     * @return
     */
    public static BufferedImage addWatermark(BufferedImage bufferedImage, Font font, Color color, WatermarkTypeEnum watermarkType,
                                             WatermarkPositionEnum imageWatermarkPositionType, String watermark) {
        // 图片宽度
        Integer imageWidth = bufferedImage.getWidth();
        // 图片高度
        Integer imageHeight = bufferedImage.getHeight();
        // 创建画笔
        Graphics2D graphics = bufferedImage.createGraphics();

        WatermarkInfo watermarkInfo = getWatermarkInfo(graphics, watermarkType, watermark, font, color);

        switch (imageWatermarkPositionType) {
            // 居中
            case CENTER:
                centerCompute(graphics, imageWidth, imageHeight, watermarkInfo, watermarkType, watermark);
                break;
            // 左侧
            case LEFT_SIDE:
                leftSideCompute(graphics, imageHeight, watermarkInfo, watermarkType, watermark);
                break;
            // 右侧
            case RIGHT_SIDE:
                rightSideCompute(graphics, imageWidth, imageHeight, watermarkInfo, watermarkType, watermark);
                break;
            // 正上方
            case DIRECTLY_ABOVE:
                directlyAboveCompute(graphics, imageWidth, watermarkInfo, watermarkType, watermark);
                break;
            // 正下方
            case DIRECTLY_BELOW:
                directlyBelowCompute(graphics, imageWidth, imageHeight, watermarkInfo, watermarkType, watermark);
                break;
            // 左上角
            case UPPER_LEFT:
                upperLeftCompute(graphics, watermarkInfo, watermarkType, watermark);
                break;
            // 左下角
            case LOWER_LEFT:
                lowerLeftCompute(graphics, imageHeight, watermarkInfo, watermarkType, watermark);
                break;
            // 右上角
            case UPPER_RIGHT:
                upperRightCompute(graphics, imageWidth, watermarkInfo, watermarkType, watermark);
                break;
            // 右下角
            case LOWER_RIGHT:
                lowerRightCompute(graphics, imageWidth, imageHeight, watermarkInfo, watermarkType, watermark);
                break;
            // 右下角
            case TILE:
                tileDrawCompute(graphics, imageWidth, imageHeight, watermarkInfo, watermarkType, watermark, 2);
                break;
            // 右下角
            case TILT_TILE:
                // 倾斜度
                graphics.rotate(0.2);
                tileDrawCompute(graphics, imageWidth, imageHeight, watermarkInfo, watermarkType, watermark, 5);
                break;
            default: // 错误的操作类型
                throw new RuntimeException("错误的操作类型");
        }
        graphics.dispose();
        return bufferedImage;
    }

    /**
     * 居中 配置
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarkInfo
     * @param watermarkType
     * @param watermark
     */
    public static void centerCompute(Graphics2D graphics, Integer imageWidth, Integer imageHeight,
                                     WatermarkInfo watermarkInfo, WatermarkTypeEnum watermarkType, String watermark) {
        // 设置水印的坐标(为原图片中间位置)
        Integer x = (imageWidth - watermarkInfo.getWatermarkWidth()) / 2;
        Integer y = imageHeight / 2 + (watermarkInfo.getWatermarkHeight() / 2);
        drawTestOrImage(graphics, x, y, watermarkType, watermark, watermarkInfo);
    }

    /**
     * 左侧 配置
     *
     * @param graphics
     * @param imageHeight
     * @param watermarkInfo
     * @param watermarkType
     * @param watermark
     */
    public static void leftSideCompute(Graphics2D graphics, Integer imageHeight, WatermarkInfo watermarkInfo,
                                       WatermarkTypeEnum watermarkType, String watermark) {
        // 设置水印的坐标(为原图片中间位置)
        Integer x = 0;
        Integer y = (imageHeight + watermarkInfo.getWatermarkHeight()) / 2;
        drawTestOrImage(graphics, x, y, watermarkType, watermark, watermarkInfo);
    }

    /**
     * 右侧配置
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarkInfo
     * @param watermarkType
     * @param watermark
     */
    public static void rightSideCompute(Graphics2D graphics, Integer imageWidth, Integer imageHeight,
                                        WatermarkInfo watermarkInfo, WatermarkTypeEnum watermarkType, String watermark) {
        // 设置水印的坐标(为原图片中间位置)
        Integer x = imageWidth - watermarkInfo.getWatermarkWidth();
        Integer y = (imageHeight + watermarkInfo.getWatermarkHeight()) / 2;

        drawTestOrImage(graphics, x, y, watermarkType, watermark, watermarkInfo);
    }

    /**
     * 正上方配置
     *
     * @param graphics
     * @param imageWidth
     * @param watermarkInfo
     * @param watermarkType
     * @param watermark
     */
    public static void directlyAboveCompute(Graphics2D graphics, Integer imageWidth, WatermarkInfo watermarkInfo,
                                            WatermarkTypeEnum watermarkType, String watermark) {
        // 设置水印的坐标(为原图片中间位置)
        Integer x = (imageWidth - watermarkInfo.getWatermarkWidth()) / 2;
        Integer y = watermarkInfo.getWatermarkHeight();

        drawTestOrImage(graphics, x, y, watermarkType, watermark, watermarkInfo);
    }

    /**
     * 正下方
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarkInfo
     * @param watermarkType
     * @param watermark
     */
    public static void directlyBelowCompute(Graphics2D graphics, Integer imageWidth, Integer imageHeight,
                                            WatermarkInfo watermarkInfo, WatermarkTypeEnum watermarkType, String watermark) {
        // 设置水印的坐标(为原图片中间位置)
        Integer x = (imageWidth - watermarkInfo.getWatermarkWidth()) / 2;
        Integer y = imageHeight;

        drawTestOrImage(graphics, x, y, watermarkType, watermark, watermarkInfo);
    }

    /**
     * 左上角
     *
     * @param graphics
     * @param watermarkInfo
     * @param watermarkType
     * @param watermark
     */
    public static void upperLeftCompute(Graphics2D graphics, WatermarkInfo watermarkInfo, WatermarkTypeEnum watermarkType,
                                        String watermark) {
        // 设置水印的坐标(为原图片中间位置)
        Integer x = 0;
        Integer y = watermarkInfo.getWatermarkHeight();

        drawTestOrImage(graphics, x, y, watermarkType, watermark, watermarkInfo);
    }

    /**
     * 左下角
     *
     * @param graphics
     * @param imageHeight
     * @param watermarkInfo
     * @param watermarkType
     * @param watermark
     */
    public static void lowerLeftCompute(Graphics2D graphics, Integer imageHeight, WatermarkInfo watermarkInfo,
                                        WatermarkTypeEnum watermarkType, String watermark) {
        // 设置水印的坐标(为原图片中间位置)
        Integer x = 0;
        Integer y = imageHeight;

        drawTestOrImage(graphics, x, y, watermarkType, watermark, watermarkInfo);
    }

    /**
     * 右上方
     *
     * @param graphics
     * @param imageWidth
     * @param watermarkInfo
     * @param watermarkType
     * @param watermark
     */
    public static void upperRightCompute(Graphics2D graphics, Integer imageWidth, WatermarkInfo watermarkInfo,
                                         WatermarkTypeEnum watermarkType, String watermark) {
        // 设置水印的坐标(为原图片中间位置)
        Integer x = imageWidth - watermarkInfo.getWatermarkWidth();
        Integer y = watermarkInfo.getWatermarkHeight();

        drawTestOrImage(graphics, x, y, watermarkType, watermark, watermarkInfo);
    }

    /**
     * 右下方
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarkInfo
     * @param watermarkType
     * @param watermark
     */
    public static void lowerRightCompute(Graphics2D graphics, Integer imageWidth, Integer imageHeight,
                                         WatermarkInfo watermarkInfo, WatermarkTypeEnum watermarkType, String watermark) {

        // 设置水印的坐标(为原图片中间位置)
        Integer x = imageWidth - watermarkInfo.getWatermarkWidth();
        Integer y = imageHeight;

        drawTestOrImage(graphics, x, y, watermarkType, watermark, watermarkInfo);
    }


    /**
     * 平铺
     *
     * @param graphics
     * @param imageWidth
     * @param imageHeight
     * @param watermarkInfo
     * @param watermarkType
     * @param watermark
     * @param splitMultiple 间隔倍数
     */
    public static void tileDrawCompute(Graphics2D graphics, Integer imageWidth, Integer imageHeight,
                                       WatermarkInfo watermarkInfo, WatermarkTypeEnum watermarkType, String watermark, Integer splitMultiple) {

        Integer watermarkHeight = watermarkInfo.getWatermarkHeight();
        Integer watermarkWidth = watermarkInfo.getWatermarkWidth();
        // 间隔
        Integer split = watermarkHeight * splitMultiple;

        // x,y可以绘制的数量,多加一个补充空白
        int xCanNum = imageWidth / watermarkWidth + 1;
        int yCanNum = imageHeight / watermarkHeight + 1;
        for (int i = 1; i <= yCanNum; i++) {
            int y = watermarkHeight * i + split * i;
            for (int j = 0; j < xCanNum; j++) {
                int x = watermarkWidth * j + split * j;
                drawTestOrImage(graphics, x, y - (watermarkHeight + split) * j, watermarkType, watermark,
                        watermarkInfo);
            }
        }
    }


    /**
     * 画文字水印或图片水印
     *
     * @param graphics
     * @param x
     * @param y
     * @param watermarkType
     * @param watermark
     * @param watermarkInfo
     */
    public static void drawTestOrImage(Graphics2D graphics, int x, int y, WatermarkTypeEnum watermarkType, String watermark,
                                       WatermarkInfo watermarkInfo) {

        // 文字水印
        if (WatermarkTypeEnum.TEXT_WATERMARK.equals(watermarkType)) {
            graphics.drawString(watermark, x, y);
        } else {
            // 图片水印
            graphics.drawImage(watermarkInfo.getWatermarkBufferedImage(), x, y - watermarkInfo.getWatermarkHeight(), watermarkInfo.getWatermarkWidth(),
                    watermarkInfo.getWatermarkHeight(), null);
        }
    }


    /**
     * 获取水印文字的长度
     *
     * @param watermarkContent 文字水印内容
     * @param graphics         图像类
     * @return
     */
    private static Integer getWatermarkLength(String watermarkContent, Graphics2D graphics) {
        return graphics.getFontMetrics(graphics.getFont()).charsWidth(watermarkContent.toCharArray(), 0,
                watermarkContent.length());
    }


    /**
     * 将图片写入到指定位置
     *
     * @param bufImg
     * @param tarImgPath
     */
    public static void writeImage(BufferedImage bufImg, String tarImgPath) {
        // 输出图片
        try {
            FileOutputStream outImgStream = new FileOutputStream(tarImgPath);
            ImageIO.write(bufImg, "png", outImgStream);
            outImgStream.flush();
            outImgStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 读取图片
     *
     * @param path
     * @return
     */
    public static BufferedImage readPicture(String path) {
        try {
            // 尝试获取本地
            return readLocalPicture(path);
        } catch (Exception e) {
            // 尝试获取网路
            return readNetworkPicture(path);
        }
    }


    /**
     * 读取本地图片
     *
     * @param path
     * @return
     */
    public static BufferedImage readLocalPicture(String path) {
        if (null == path) {
            throw new RuntimeException("本地图片路径不能为空");
        }
        // 读取原图片信息 得到文件
        File srcImgFile = new File(path);
        try {
            // 将文件对象转化为图片对象
            return ImageIO.read(srcImgFile);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 读取网络图片
     *
     * @param path 网络图片地址
     */
    public static BufferedImage readNetworkPicture(String path) {
        if (null == path) {
            throw new RuntimeException("网络图片路径不能为空");
        }
        try {
            // 创建一个URL对象,获取网络图片的地址信息
            URL url = new URL(path);
            // 将URL对象输入流转化为图片对象 (url.openStream()方法，获得一个输入流)
            BufferedImage bugImg = ImageIO.read(url.openStream());
            if (null == bugImg) {
                throw new RuntimeException("网络图片地址不正确");
            }
            return bugImg;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 水印信息对象
     */
    private static class WatermarkInfo {

        BufferedImage watermarkBufferedImage;
        Integer watermarkWidth;
        Integer watermarkHeight;

        public BufferedImage getWatermarkBufferedImage() {
            return watermarkBufferedImage;
        }

        public void setWatermarkBufferedImage(BufferedImage watermarkBufferedImage) {
            this.watermarkBufferedImage = watermarkBufferedImage;
        }

        public Integer getWatermarkWidth() {
            return watermarkWidth;
        }

        public void setWatermarkWidth(Integer watermarkWidth) {
            this.watermarkWidth = watermarkWidth;
        }

        public Integer getWatermarkHeight() {
            return watermarkHeight;
        }

        public void setWatermarkHeight(Integer watermarkHeight) {
            this.watermarkHeight = watermarkHeight;
        }
    }


}
